package cn.net.xyan.yypt.demo

import cn.net.xyan.blossom.kotlin.StatusAndMessageError
import cn.net.xyan.blossom.kotlin.logger
import com.fasterxml.jackson.dataformat.xml.XmlMapper
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory
import java.io.StringWriter
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import java.lang.reflect.Proxy
import java.net.URL
import java.util.*
import javax.jws.WebMethod

@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS,AnnotationTarget.TYPE)
annotation class XmlCollectionRootElement(val localName: String)

fun URL.wsInvoke(methodName:String, vararg args:Any?):Array<Any?>{
    val dcf = JaxWsDynamicClientFactory.newInstance()
    val client = dcf.createClient(this)
    return client.invoke(methodName,args)
}

fun <T> createInstance(wsdl: URL,interfaceType:Class<T>,xmlMapper: XmlMapper? = null):T{
    val proxy = XMLStringWsInvocationHandler(wsdl,xmlMapper)

    return Proxy.newProxyInstance(XMLStringWsInvocationHandler::class.java.classLoader,
            arrayOf(interfaceType),proxy) as T
}


class XMLStringWsInvocationHandler(val wsdl:URL,mapper: XmlMapper? = null): InvocationHandler {

    val dcf = JaxWsDynamicClientFactory.newInstance()
    val client = dcf.createClient(wsdl)
    val xmlMapper = mapper?: XmlMapper()

    override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? {
        var m = method?:throw StatusAndMessageError("method is null")
        var methodName = m.name
        if (m.isAnnotationPresent(WebMethod::class.java)){
            methodName = m.getDeclaredAnnotation(WebMethod::class.java).operationName
        }


        var req:MutableList<String?> = LinkedList()
        args?.forEach {
            val p = if (it == null){
                null
            }else {
                it as? String ?: if (it is Collection<*>) {
                    if (it.isNotEmpty()) {
                        val first = it.first()
                        val output = StringWriter()

                        it.forEach {
                            xmlMapper.writeValue(output, it)
                        }

                        var rootName = "list"

                        if (true == first?.javaClass?.isAnnotationPresent(XmlCollectionRootElement::class.java)) {
                            val annotation = first.javaClass.getAnnotation(XmlCollectionRootElement::class.java)
                            rootName = annotation.localName
                        }
                        "<$rootName>${output.toString()}</$rootName>"

                    } else {
                        xmlMapper.writeValueAsString(it)
                    }
                } else {
                    xmlMapper.writeValueAsString(it)
                }
            }

            req.add(p)
        }

        logger.info("call methodName:$methodName\n$req")

        var result = client.invoke(methodName, *req.toTypedArray())



        if (result.size != 1){
            throw StatusAndMessageError("返回的结果个数不等于1")
        }

        val rsp = result.first() as? String ?: throw StatusAndMessageError("返回的结果类型不为String")
        logger.info("result:$rsp")
        return when {
            method.returnType == String::class.java -> return rsp
            method.returnType != Unit::class.java -> {
                val returnType = xmlMapper.typeFactory.constructType(method.genericReturnType)
                xmlMapper.readValue<Any?>(rsp, returnType)
            }
            else -> Unit
        }

    }

}